// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "Components/StaticMeshComponent.h"
#include "StaticMeshResources.h"
#include "Engine/StaticMesh.h"
#include "Engine/StaticMeshActor.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "Kismet/GameplayStatics.h"
#include "MeshToOBJExporter.generated.h"

UCLASS()
class MYSCENE_API AMeshToOBJExporter : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AMeshToOBJExporter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

public:
	 
	UFUNCTION(BlueprintCallable, Category = "Mesh Export")
	bool ExportStaticMeshToOBJ(UStaticMesh* StaticMesh, const FString& OutputPath, bool bExportMaterial = false)
	{
		if (!StaticMesh)
		{
			UE_LOG(LogTemp, Error, TEXT("StaticMesh is null!"));
			return false;
		}

		TArray<FVector> Vertices;
		TArray<FVector> Normals;
		TArray<FVector2D> UVs;
		TArray<FProcMeshTangent> Tangents;
		TArray<int32> Triangles;

		GetMeshData(StaticMesh, Vertices, Normals, UVs, Tangents, Triangles);

		if (Vertices.Num() == 0 || Triangles.Num() == 0)
		{
			UE_LOG(LogTemp, Error, TEXT("No valid mesh data found!"));
			return false;
		}

		FString MaterialName = "";
		if (bExportMaterial && StaticMesh->GetStaticMaterials().Num() > 0)
		{
			MaterialName = StaticMesh->GetStaticMaterials()[0].MaterialSlotName.ToString();
		}
		bool bSuccess = WriteOBJFile(OutputPath, Vertices, Normals, UVs, Triangles, MaterialName);;
		
		

		if (bSuccess)
		{
			UE_LOG(LogTemp, Log, TEXT("Successfully exported mesh to: %s"), *OutputPath);
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to export mesh to: %s"), *OutputPath);
		}

		return bSuccess;
	};

	bool ExportAllStaticMeshesInLevel(const FString& OutputDirectory)
	{
		TArray<AActor*> FoundActors;
		UGameplayStatics::GetAllActorsOfClass(GetWorld(), AStaticMeshActor::StaticClass(), FoundActors);

		if (FoundActors.Num() == 0)
		{
			UE_LOG(LogTemp, Warning, TEXT("No StaticMeshActors found in the level."));
			return false;
		}

		bool bAnyExported = false;

		for (AActor* Actor : FoundActors)
		{
			AStaticMeshActor* StaticMeshActor = Cast<AStaticMeshActor>(Actor);
			if (StaticMeshActor && StaticMeshActor->GetStaticMeshComponent() && StaticMeshActor->GetStaticMeshComponent()->GetStaticMesh())
			{
				UStaticMesh* StaticMesh = StaticMeshActor->GetStaticMeshComponent()->GetStaticMesh();
				FString MeshName = StaticMesh->GetName();
				FString FilePath = OutputDirectory + "/" + MeshName + ".obj";

				if (ExportStaticMeshToOBJ(StaticMesh, FilePath, true))
				{
					bAnyExported = true;
					UE_LOG(LogTemp, Log, TEXT("Exported: %s"), *MeshName);
				}
			}
		}

		return bAnyExported;
	}


private:
	// 获取Mesh的顶点数据
	void GetMeshData(UStaticMesh* StaticMesh, TArray<FVector>& OutVertices, TArray<FVector>& OutNormals,
		TArray<FVector2D>& OutUVs, TArray<FProcMeshTangent>& OutTangents, TArray<int32>& OutTriangles) {
		if (!StaticMesh || !StaticMesh->GetRenderData())
		{
			return;
		}

		const FStaticMeshLODResources& LODModel = StaticMesh->GetRenderData()->LODResources[0];
		const FPositionVertexBuffer& VertexBuffer = LODModel.VertexBuffers.PositionVertexBuffer;
		const FStaticMeshVertexBuffer& StaticMeshVertexBuffer = LODModel.VertexBuffers.StaticMeshVertexBuffer;
		const FRawStaticIndexBuffer& IndexBuffer = LODModel.IndexBuffer;

		// 获取顶点数据
		const uint32 VertexCount = VertexBuffer.GetNumVertices();
		for (uint32 VertIndex = 0; VertIndex < VertexCount; ++VertIndex)
		{
			const FVector& Vertex = VertexBuffer.VertexPosition(VertIndex);
			OutVertices.Add(Vertex);
		}

		for (uint32 VertIndex = 0; VertIndex < VertexCount; ++VertIndex)
		{
			const FVector& Normal = StaticMeshVertexBuffer.VertexTangentZ(VertIndex);
			OutNormals.Add(Normal);

			const FVector2D& UV = StaticMeshVertexBuffer.GetVertexUV(VertIndex, 0); // 使用第一个UV通道
			OutUVs.Add(UV);

			FProcMeshTangent Tangent;
			Tangent.TangentX = StaticMeshVertexBuffer.VertexTangentX(VertIndex);
			OutTangents.Add(Tangent);
		}

		// 获取三角形数据
		const uint32 TriangleCount = LODModel.GetNumTriangles();
		for (uint32 TriangleIndex = 0; TriangleIndex < TriangleCount; ++TriangleIndex)
		{
			uint32 Index0 = IndexBuffer.GetIndex(TriangleIndex * 3 + 0);
			uint32 Index1 = IndexBuffer.GetIndex(TriangleIndex * 3 + 1);
			uint32 Index2 = IndexBuffer.GetIndex(TriangleIndex * 3 + 2);

			OutTriangles.Add(Index0);
			OutTriangles.Add(Index1);
			OutTriangles.Add(Index2);
		}

		
	}

#pragma region MyRegion
	// 写入OBJ文件
	//bool WriteOBJFile(const FString& Filename, const TArray<FVector>& Vertices, const TArray<FVector>& Normals,
	//	const TArray<FVector2D>& UVs, const TArray<int32>& Triangles, const FString& MaterialName = "") {
	//	FString OBJContent;

	//	// 写入顶点
	//	for (const FVector& Vertex : Vertices)
	//	{
	//		OBJContent += FString::Printf(TEXT("v %f %f %f\n"), Vertex.X, Vertex.Y, Vertex.Z);
	//	}

	//	// 写入UVs
	//	for (const FVector2D& UV : UVs)
	//	{
	//		OBJContent += FString::Printf(TEXT("vt %f %f\n"), UV.X, UV.Y);
	//	}

	//	// 写入法线
	//	for (const FVector& Normal : Normals)
	//	{
	//		OBJContent += FString::Printf(TEXT("vn %f %f %f\n"), Normal.X, Normal.Y, Normal.Z);
	//	}

	//	// 写入材质引用
	//	if (!MaterialName.IsEmpty())
	//	{
	//		OBJContent += FString::Printf(TEXT("usemtl %s\n"), *MaterialName);
	//	}

	//	// 写入面
	//	for (int32 i = 0; i < Triangles.Num(); i += 3)
	//	{
	//		int32 Index0 = Triangles[i] + 1;
	//		int32 Index1 = Triangles[i + 1] + 1;
	//		int32 Index2 = Triangles[i + 2] + 1;

	//		OBJContent += FString::Printf(TEXT("f %d/%d/%d %d/%d/%d %d/%d/%d\n"),
	//			Index0, Index0, Index0,
	//			Index1, Index1, Index1,
	//			Index2, Index2, Index2);
	//	}

	//	// 确保目录存在
	//	FString Directory = FPaths::GetPath(Filename);
	//	if (!IFileManager::Get().DirectoryExists(*Directory))
	//	{
	//		IFileManager::Get().MakeDirectory(*Directory, true);
	//	}

	//	// 写入文件
	//	return FFileHelper::SaveStringToFile(OBJContent, *Filename);
	//}
#pragma endregion

	bool WriteOBJFile(const FString& Filename, const TArray<FVector>& Vertices, const TArray<FVector>& Normals,
		const TArray<FVector2D>& UVs, const TArray<int32>& Triangles, const FString& MaterialName = "") {
		FString OBJContent;

		auto ConvertPosition = [](const FVector& Vec) {
			// Unreal (X,Y,Z) -> OBJ (X,-Z,Y)
			//return FVector(Vec.X, -Vec.Z, Vec.Y);
			return FVector(Vec.X, Vec.Z, -Vec.Y);
			};

		auto ConvertNormal = [](const FVector& Normal) {
			//FVector Converted(Normal.X, -Normal.Z, Normal.Y);
			FVector Converted(-Normal.X, -Normal.Z, Normal.Y);
			return Converted.GetSafeNormal();
			};

		// 写入顶点
		for (const FVector& Vertex : Vertices)
		{
			FVector Converted = ConvertPosition(Vertex);
			OBJContent += FString::Printf(TEXT("v %f %f %f\n"),
				Converted.X, Converted.Y, Converted.Z);
		}

		// 写入UVs (V坐标翻转)
		for (const FVector2D& UV : UVs)
		{
			OBJContent += FString::Printf(TEXT("vt %f %f\n"), UV.X, 1.0f - UV.Y);
		}

		// 写入法线
		for (const FVector& Normal : Normals)
		{
			FVector ConvertedNormal = ConvertNormal(Normal);
			OBJContent += FString::Printf(TEXT("vn %f %f %f\n"),
				-ConvertedNormal.X,
				-ConvertedNormal.Y,
				-ConvertedNormal.Z);
		}

		if (!MaterialName.IsEmpty())
		{
			OBJContent += FString::Printf(TEXT("usemtl %s\n"), *MaterialName);
		}

		// 确保三角形顶点顺序一致（逆时针）
		for (int32 i = 0; i < Triangles.Num(); i += 3)
		{
			int32 Index0 = Triangles[i] + 1;
			int32 Index1 = Triangles[i + 1] + 1;
			int32 Index2 = Triangles[i + 2] + 1;

			// 保持原始顺序（Unreal默认使用顺时针顺序）
			OBJContent += FString::Printf(TEXT("f %d/%d/%d %d/%d/%d %d/%d/%d\n"),
				Index0, Index0, Index0,
				Index2, Index2, Index2,
				Index1, Index1, Index1);
		}

		// 确保目录存在
		FString Directory = FPaths::GetPath(Filename);
		if (!IFileManager::Get().DirectoryExists(*Directory))
		{
			IFileManager::Get().MakeDirectory(*Directory, true);
		}

		// 写入文件
		return FFileHelper::SaveStringToFile(OBJContent, *Filename);
	}

};
